package com.lhl.netty.server;

import com.lhl.netty.common.*;
import com.lhl.netty.server.listener.ClientMsgEvent;
import com.lhl.netty.server.listener.ClientMsgListener;
import com.lhl.netty.server.ssl.SslContextFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.util.concurrent.TimeUnit;

public abstract class NetServer {
    private final Logger logger = LoggerFactory.getLogger(NetServer.class);
    private int port;
    private String serverName;
    private Thread runThread;//用线程启动运行服务

    public boolean serverOnline = false; //服务器是否开启
    public int currentlCientCount = 0; //当前在线数

    //连接数设置
    private int maxclient = 128;
    //心跳包设置
    private int readerIdleTime = 30; //未读状态触发时间(S)
    private int writerIdleTime = 30; //未写状态触发时间(S)
    private int allIdleTime = 25; //未读写状态触发时间(S)

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ServerBootstrap b;
    private ChannelFuture f;

    private ClientMsgListener clientMsgListener;

    private BaseSession baseSession;

    protected abstract void onStart(int listenPort);
    protected abstract void newClient();
    protected abstract void disConnected();
    protected abstract void reciveMsg(ClientMsgEvent event);

    public NetServer(BaseSession baseSession, int port,String serverName) {
        this.baseSession = baseSession;
        this.serverName = serverName;
    	this.port = port;
        //向p1注册一个监听器
        this.register(new ClientMsgListener() {

            @Override
            public void onNewClient() {
                newClient();
            }

            @Override
            public void onDisConnected() {
                disConnected();
            }

            @Override
            public void onReciveMsg(ClientMsgEvent e) {
                reciveMsg(e);
            }
        });

        try {
            if(!serverOnline){
                runThread = new Thread(serverRun);
                runThread.start();
            }
        } catch (Exception e) {
            logger.error(ExceptionUtil.getMessage(e));
        }
    }

    /**
     * 发送消息给指定客户端
     * @param clientKey
     * @param msg
     */
    public void sendMsg(String clientKey,AppMsg msg){
        Channel ch = baseSession.findChannelByKey(clientKey);
        sendMsg(ch,msg);
    }

    /**
     * 发送消息给指定客户端
     * @param channel
     * @param msg
     */
    public void sendMsg(Channel channel,AppMsg msg){
        send2Client(channel,new NetMessage(NetMessageType.MESSAGE_USER_INPUT,msg.toJSON()));
    }

    /**
     * 停止服务器
     */
    protected void stopServer(){
        try {
            stop();
        } catch (Exception e) {
            logger.error(ExceptionUtil.getMessage(e));
        }
    }

    private void stop() throws Exception {
    	workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
        f.channel().close();
        logger.info(this.serverName+"已关闭");
        serverOnline = false;
        runThread.interrupt();
        runThread.join();
    }

    /**
     * 服务端消息处理
     * @param incoming
     * @param msg
     */
    void routing(Channel incoming, NetMessage msg){
        try{
            switch (msg.getType()){
                //新客户端加入
                case MESSAGE_WELCOME:{
                    if(clientMsgListener!=null){
                        clientMsgListener.onNewClient();
                    }
                    send2Client(incoming,new NetMessage(NetMessageType.MESSAGE_WELCOME,"欢迎,你已连接至服务器"));
                    break;
                }
                //客户端已断线
                case MESSAGE_DISCONNECTED:{
                    if(clientMsgListener!=null){
                        clientMsgListener.onDisConnected();
                    }
                    break;
                }
                //客户端主动发送的消息
                case MESSAGE_USER_INPUT:{
                    if(clientMsgListener!=null){
                        clientMsgListener.onReciveMsg(new ClientMsgEvent(incoming,msg.getContent()));
                    }
                    break;
                }
                default:{
                    break;
                }
            }
        }catch(Exception e){
            logger.error(ExceptionUtil.getMessage(e));
            send2Client(incoming,new NetMessage(NetMessageType.MESSAGE_SERVER_ERROR,null));
        }
    }

    private void send2Client(Channel client,NetMessage msg){
        if(client!=null){
            client.writeAndFlush(msg);
        }
    }

    //注册一个监听器
    private void register(ClientMsgListener clientMsgListener){
        this.clientMsgListener = clientMsgListener;
    }


    public int getPort() {
        return port;
    }

    public String getServerName() {
        return serverName;
    }


    private Runnable serverRun = new Runnable() {
        @Override
        public void run() {
            try {
                final SSLContext sslCtx = SslContextFactory.getServerContext();

                bossGroup = new NioEventLoopGroup(); // (1)
                workerGroup = new NioEventLoopGroup();

                b = new ServerBootstrap(); // (2)
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class) // (3)
                        .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                //ssl通讯
                            /*SSLEngine sslEngine = sslCtx.createSSLEngine();
                            sslEngine.setUseClientMode(false); //服务器端模式
                            sslEngine.setNeedClientAuth(false); //需要验证客户端
                            ch.pipeline().addLast("ssl", new SslHandler(sslEngine));*/

                                //服务端检测客户端是否失联的心跳包,第一个参数是指定读操作空闲秒数，第二个参数是指定写操作的空闲秒数，第三个参数是指定读写空闲秒数
                            /*服务端在20s内未进行读写操作，就会向客户端发送心跳包，客户端收到心跳包后立即回复心跳包给服务端，此时服务端就进行了读操作，
                            也就不会触发IdleState.READER_IDLE（未读操作状态），若客户端异常掉线了，并不能响应服务端发来的心跳包，在30s后就会触发IdleState.READER_IDLE（未读操作状态），
                            此时服务器就会将通道关闭。*/
                                ch.pipeline().addLast("ping", new IdleStateHandler(readerIdleTime, writerIdleTime, allIdleTime, TimeUnit.SECONDS));

                                //收发消息处理
                                ch.pipeline().addLast(new NetEncoder()
                                        ,new NetDecoder()
                                        ,new NetServerHandler(NetServer.this));
                            }
                        })
                        .option(ChannelOption.SO_BACKLOG, maxclient)          // (5)
                        .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

                // Bind and start to accept incoming connections.
                f = b.bind(port).sync(); // (7)

                onStart(port);
                serverOnline = true;

                // Wait until the server socket is closed.
                // In this example, this does not happen, but you can do that to gracefully
                // shut down your server.
                f.channel().closeFuture().sync();
            } catch (Exception e){
                logger.error(ExceptionUtil.getMessage(e));
            } finally {
                workerGroup.shutdownGracefully();
                bossGroup.shutdownGracefully();
            }
        }
    };
}